/*************************************************
 * 
 * Copyright by Bruno Eberhard
 * 
 * Created on 29.07.2005
 *
 *************************************************/
package fightplan.views.tables;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.binding.value.ValueModel;

import fightplan.actions.UndoRedoManager;
import fightplan.actions.UndoRedoManagerProvider;
import fightplan.actions.UndoableAction;
import fightplan.business.AbstractFighter;
import fightplan.business.Category;
import fightplan.business.CategoryStatus;
import fightplan.business.CategoryType;
import fightplan.business.Fighter;
import fightplan.business.Gender;
import fightplan.business.Sheet;
import fightplan.business.Tournament;
import fightplan.business.TournamentHolder;
import fightplan.business.TournamentListener;
import fightplan.core.FPExceptionHandler;
import fightplan.core.Style;
import fightplan.print.DrawingPrintable;
import fightplan.ui.BeanListTableModel;
import fightplan.ui.BeanPropertyTableCellRenderer;
import fightplan.ui.IconProviderRenderer;
import fightplan.views.MainView;

@SuppressWarnings("serial")
public class CategoryTable extends JPanel implements PropertyChangeListener, UndoRedoManagerProvider {
	private TournamentHolder tournamentHolder;
	private JTable table;
	private JCheckBox showDetailsCheckBox;
	private BeanListTableModel<Category> tableModel;
	private ValueModel selectedCategory;

	private TableColumn columnIcon, columnName, columnType, columnGender, columnAge, columnWeight, columnBelt, columnCount;

	public CategoryTable(final TournamentHolder tournamentHolder) {
		super(new BorderLayout());
		this.tournamentHolder = tournamentHolder;

		tournamentHolder.addValueChangeListener(this);

		selectedCategory = new ValueHolder(null);

		showDetailsCheckBox = new JCheckBox("Details anzeigen");
		table = new JTable() {
			@Override
			public void editingStopped(ChangeEvent e) {
				super.editingStopped(e);
				selectedCategory.setValue(null);
				updateSelectedCategory(); // Update Category Name for example
			}
		};
		table.setRowHeight(20);
		
		createTableModel();
		updateTableModel();

		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				updateSelectedCategory();
			}
		});
		table.putClientProperty(fightplan.actions.SelectAllAction.MENU_CONTROLLED, Boolean.TRUE);

		JScrollPane scrollPane = new JScrollPane(table);
		scrollPane.setPreferredSize(new Dimension(5000, 5000));
		scrollPane.getViewport().setBackground(Style.UNUSED_TABLE_SPACE);
		scrollPane.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				JTable table = (JTable) ((JScrollPane) e.getSource()).getViewport().getView();
				table.getSelectionModel().clearSelection();
				table.removeEditor();
			}
		});
		add(scrollPane, BorderLayout.CENTER);
		add(categoriesAction(), BorderLayout.SOUTH);

		tournamentHolder.addListener(new TournamentListener() {
			public void tournamentChanged() {
				updateTableModel();
			}
		});

		add(showDetailsCheckBox, BorderLayout.NORTH);
		showDetailsCheckBox.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				updateTableModel();
			}
		});
	}

	private void updateSelectedCategory() {
		if (table.getSelectedRowCount() == 1) {
			selectedCategory.setValue(tableModel.getList().get(table.getSelectedRow()));
		} else {
			selectedCategory.setValue(null);
		}
	}

	public ValueModel getSelectedCategory() {
		return selectedCategory;
	}

	private JPanel categoriesAction() {
		JPanel panel = new JPanel(new BorderLayout());
		panel.add(addRemoveAction(), BorderLayout.EAST);
		panel.add(drawAction(), BorderLayout.SOUTH);
		return panel;
	}

	private JPanel addRemoveAction() {
		JPanel panel = new JPanel(new FlowLayout(SwingConstants.RIGHT));
		panel.add(createSplitButton());
		panel.add(createMergeButton());
		panel.add(createDeleteButton());
		panel.add(createNewButton());
		return panel;
	}

	private JPanel drawAction() {
		JPanel panel = new JPanel(new FlowLayout(SwingConstants.RIGHT));
		panel.add(createUndrawButton());
		panel.add(createPrintButton());
		panel.add(createDrawButton());

		return panel;
	}

	private Tournament getTournament() {
		return (Tournament) tournamentHolder.getValue();
	}

	public UndoRedoManager getUndoRedoManager() {
		return ((MainView) SwingUtilities.getWindowAncestor(this)).getUndoRedoManager();
	}

	private void createTableModel() {
		tableModel = new BeanListTableModel<Category>(null, new String[] { "status", "nameOrDescription",
				"categoryType", "gender", "ageRangeFormatted", "weightRangeFormatted", "beltRangeFormatted",
				"fighters.size" }, new String[] { "", "Kategorie", "Art", "D/H", "Alter", "Gewicht", "Gurt",
				"Anmeldungen" }) {

			public boolean isCellEditable(int rowIndex, int columnIndex) {
				Category category = getList().get(rowIndex);
				return super.isCellEditable(rowIndex, columnIndex) && !category.isPlanned();
			}

		};
		table.setModel(tableModel);
		tableModel.setColumnReadOnly(0);
		tableModel.setColumnReadOnly(1);
		tableModel.setColumnReadOnly(7);
		TableColumnModel cm = table.getColumnModel();

		columnIcon = cm.getColumn(0);
		columnName = cm.getColumn(1);
		columnType = cm.getColumn(2);
		columnGender = cm.getColumn(3);
		columnAge = cm.getColumn(4);
		columnWeight = cm.getColumn(5);
		columnBelt = cm.getColumn(6);
		columnCount = cm.getColumn(7);
		
		columnIcon.setCellRenderer(new IconProviderRenderer());
		columnIcon.setPreferredWidth(22);
		columnIcon.setMaxWidth(22);
		columnType.setCellRenderer(new BeanPropertyTableCellRenderer("name"));
		columnType.setCellEditor(new CategoryTypeCellEditor(CategoryType.VALUES));
		columnGender.setCellRenderer(new BeanPropertyTableCellRenderer("nameLong"));
		columnGender.setCellEditor(new GenderCellEditor(Gender.VALUES_AND_ALL));
		columnCount.setPreferredWidth(80);
		columnCount.setMaxWidth(85);
	}
	
	private void updateTableModel() {
		Tournament t = getTournament();
		TableColumnModel cm = table.getColumnModel();

		// remove all but first column
		for (int i = table.getColumnCount()-1;  i>0; i--) {
			cm.removeColumn(cm.getColumn(i));
		}
		
		boolean showDetails = showDetailsCheckBox.isSelected();
		cm.addColumn(columnName);
		if (showDetails) {
			cm.addColumn(columnType);
			cm.addColumn(columnGender);
			cm.addColumn(columnAge);
			if (t == null || t.getMode().hasWeights()) {
				cm.addColumn(columnWeight);
			}
			if (t == null || t.getMode().hasBelts()) {
				cm.addColumn(columnBelt);
			}
		}
		cm.addColumn(columnCount);
	}

	private JButton createNewButton() {
		Action addCategoryAction = new UndoableAction("Hinzufügen", this, "Kategorie wieder entfernen") {
			public boolean doAction(ActionEvent e) {
				Category category = new Category();
				tableModel.addElement(category);
				return true;
			}
		};
		addCategoryAction.putValue(Action.SHORT_DESCRIPTION, "Eine neue Kategorie erstellen");
		return new JButton(addCategoryAction);
	}

	private JButton createDeleteButton() {
		final Action removeCategoryAction = new UndoableAction("Löschen", this, "Kategorie wieder herstellen") {
			public boolean doAction(ActionEvent e) {
				boolean confirm = JOptionPane.showConfirmDialog(CategoryTable.this,
						"Wollen Sie diese Kategorie(n) wirklich löschen?", "Löschen?", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION;
				if (!confirm)
					return false;
				int[] rows = table.getSelectedRows();
				for (int i = rows.length - 1; i >= 0; i--) {
					Category category = tableModel.getList().get(rows[i]);
					tableModel.removeElement(category);
				}
				return true;
			}
		};
		removeCategoryAction.setEnabled(table.getSelectedRows().length >= 1);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				removeCategoryAction.setEnabled(table.getSelectedRows().length >= 1);
			}
		});
		removeCategoryAction.putValue(Action.SHORT_DESCRIPTION, "Die selektierten Kategorien löschen");
		return new JButton(removeCategoryAction);
	}

	private JButton createMergeButton() {
		final Action mergeAction = new UndoableAction("Zusammenfügen", this, "Kategorie wieder trennen") {
			public boolean doAction(ActionEvent e) {
				merge();
				return true;
			}
		};
		mergeAction.setEnabled(mergeableSelection());
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				mergeAction.setEnabled(mergeableSelection());
			}
		});
		mergeAction.putValue(Action.SHORT_DESCRIPTION, "Die selektierten Kategorien zusammenfügen");
		return new JButton(mergeAction);
	}

	private boolean mergeableSelection() {
		if (table.getSelectedRowCount() < 2)
			return false;
		Category firstCategory = tableModel.getList().get(table.getSelectedRows()[0]);
		CategoryType type = firstCategory.getCategoryType();
		for (int i = 1; i < table.getSelectedRowCount(); i++) {
			Category category = tableModel.getList().get(table.getSelectedRows()[i]);
			if (!type.equals(category.getCategoryType()))
				return false;
		}
		return true;
	}

	private void merge() {
		int[] rows = table.getSelectedRows();
		Category firstCategory = tableModel.getList().get(table.getSelectedRows()[0]);

		for (int i = 1; i < rows.length; i++) {
			Category category = tableModel.getList().get(table.getSelectedRows()[i]);
			if (!firstCategory.getGender().comaptible(category.getGender())) {
				firstCategory.setGender(Gender.ALL);
			}
			firstCategory.setAgeStart(Math.min(firstCategory.getAgeStart(), category.getAgeStart()));
			firstCategory.setAgeEnd(Math.max(firstCategory.getAgeEnd(), category.getAgeEnd()));
			firstCategory.setMinWeight(Math.min(firstCategory.getMinWeight(), category.getMinWeight()));
			firstCategory.setMaxWeight(Math.max(firstCategory.getMaxWeight(), category.getMaxWeight()));
			firstCategory.setMinBelt(Math.min(firstCategory.getMinBelt(), category.getMinBelt()));
			firstCategory.setMaxBelt(Math.max(firstCategory.getMaxBelt(), category.getMaxBelt()));
		}

		for (int i = 1; i < rows.length; i++) {
			merge(getTournament(), firstCategory, tableModel.getList().get(rows[i]));
		}

		for (int i = rows.length - 1; i > 0; i--) {
			tableModel.removeElement(tableModel.getList().get(rows[i]));
		}
	}

	private void merge(Tournament tournament, Category category, Category c) {
		for (AbstractFighter fighter : c.getFighters()) {
			if (!category.getFighters().contains(fighter)) {
				category.getFighters().add(fighter);
			}
		}
		c.getFighters().clear();
	}

	private JButton createSplitButton() {
		final Action splitAction = new UndoableAction("Spliten", this, "Das Spliten rückgängig machen.") {
			public boolean doAction(ActionEvent e) {
				return split();
			}
		};
		splitAction.setEnabled(table.getSelectedRowCount() == 1);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				splitAction.setEnabled(table.getSelectedRowCount() == 1);
			}
		});
		splitAction.putValue(Action.SHORT_DESCRIPTION, "Trennt die aktuelle Kategorie in Gewichtsgruppen");
		return new JButton(splitAction);
	}

	private boolean split() {
		Category category = tableModel.getList().get(table.getSelectedRow());
		if (category.isTeam()) {
			FPExceptionHandler.instance().showWarning("Team Kategorien können nicht aufgeteilt werden.",
					"Aufteilung fehlgeschlagen");
			return false;
		}
		List<Fighter> fighters = new ArrayList<Fighter>(category.getFighters().size());
		for (AbstractFighter fighter : category.getFighters()) {
			Fighter f = (Fighter) fighter;
			if (f.getWeight() == 0) {
				FPExceptionHandler.instance().showWarning(
						"Es müssen zu sämtlichen Teilnehmern das Gewicht bekannt sein.\nEs fehlt beim Teilnehmer: "
								+ ((Fighter) f).getDisplayName(), "Aufteilung fehlgeschlagen");
				return false;
			}
			fighters.add(f);
		}
		int anzahl = (Integer) JOptionPane.showInputDialog(this, "In wieviele Kategorien soll geteilt werden?",
				"Gewünschte Anzahl Kategorien", JOptionPane.QUESTION_MESSAGE, null, new Object[] { 2, 3, 4, 5 }, 3);
		int fighterCount = fighters.size();
		if (anzahl > fighterCount) {
			FPExceptionHandler
					.instance()
					.showWarning(
							"Es kann nicht in mehr Kategorien geteilt werden\nals Teilnehmer in der ursprünglichen Kategorie sind.",
							"Aufteilung fehlgeschlagen");
			return false;
		}

		Collections.sort(fighters, new Comparator<Fighter>() {
			public int compare(Fighter o1, Fighter o2) {
				if (o1.getWeight() < o2.getWeight())
					return -1;
				if (o1.getWeight() > o2.getWeight())
					return 1;
				return 0;
			}
		});

		int selection = table.getSelectedRow();

		int pos = 0;
		for (int i = 0; i < anzahl; i++) {
			int last = fighterCount * (i + 1) / anzahl - 1;
			Category newCategory = new Category();
			newCategory.setAgeStart(category.getAgeStart());
			newCategory.setAgeEnd(category.getAgeEnd());
			newCategory.setCategoryType(category.getCategoryType());
			newCategory.setGender(category.getGender());
			newCategory.setMaxBelt(category.getMaxBelt());
			newCategory.setMinBelt(category.getMinBelt());
			newCategory.setMinWeight(((Fighter) fighters.get(pos)).getWeight());
			int maxWeight = ((Fighter) fighters.get(last)).getWeight();
			newCategory.setMaxWeight(maxWeight);
			while (pos <= last || pos < fighters.size() && fighters.get(pos).getWeight() <= maxWeight) {
				newCategory.getFighters().add(fighters.get(pos));
				pos++;
			}
			tableModel.addElement(selection + i, newCategory);
			if (pos >= fighters.size() && i < anzahl - 1) {
				tableModel.removeElement(category);
				FPExceptionHandler
						.instance()
						.showWarning(
								"Aufgrund zu gleichmässiger Gewichtsverteilung konnten\nnicht so viele Kategorien wie gefordert erstellt werden.",
								"Aufteilung teilweise fehlgeschlagen");
				return true;
			}
			String add;
			if (i == 0)
				add = "-" + newCategory.getMaxWeight() + " kg";
			else if (i == anzahl - 1)
				add = newCategory.getMinWeight() + "+ kg";
			else
				add = newCategory.getMinWeight() + "-" + newCategory.getMaxWeight() + " kg";
			newCategory.setName(category.getName() + " (" + add + ")");
		}
		tableModel.removeElement(category);
		return true;
	}

	private JButton createDrawButton() {
		final Action drawAction;
		drawAction = new UndoableAction("Auslosen", this, "Das Auslosen rückgängig machen") {
			public boolean doAction(ActionEvent e) {
				return draw();
			}
		};
		drawAction.setEnabled(table.getSelectedRowCount() > 0);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				enableIfAllReady(drawAction);
			}
		});
		tournamentHolder.addListener(new TournamentListener() {
			public void tournamentChanged() {
				enableIfAllReady(drawAction);
			}
		});
		drawAction.putValue(Action.SHORT_DESCRIPTION, "Startet die Auslosung zu den selektierten Kategorien");
		JButton button = new JButton(drawAction);
		button.setFont(button.getFont().deriveFont(Font.BOLD));
		return button;
	}

	private void enableIfAllReady(Action action) {
		if (table.getSelectedRowCount() > 0) {
			for (int i = 0; i < table.getSelectedRowCount(); i++) {
				Category category = tableModel.getList().get(table.getSelectedRows()[i]);
				if (!CategoryStatus.READY.equals(category.getStatus())) {
					action.setEnabled(false);
					return;
				}
			}
			action.setEnabled(true);
		} else {
			action.setEnabled(false);
		}
	}

	private boolean draw() {
		Category[] categories = new Category[table.getSelectedRowCount()];
		// boolean checked = true;
		for (int i = 0; i < table.getSelectedRowCount(); i++) {
			categories[i] = tableModel.getList().get(table.getSelectedRows()[i]);
			// checked &= checkCategory(categories[i]);
		}

		for (Category category : categories) {
			category.plan();
		}

		tournamentHolder.fireChange();
		return true;
	}

	private JButton createUndrawButton() {
		final Action undrawAction;
		undrawAction = new UndoableAction("Auslosung löschen", this, "Auslosung wiederherstellen") {
			public boolean doAction(ActionEvent e) {
				return undraw();
			}
		};
		undrawAction.setEnabled(table.getSelectedRowCount() > 0);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				enableIfNoPlanned(undrawAction);
			}
		});
		tournamentHolder.addListener(new TournamentListener() {
			public void tournamentChanged() {
				enableIfNoPlanned(undrawAction);
			}
		});
		undrawAction.putValue(Action.SHORT_DESCRIPTION, "Löscht die Auslosung der selektierten Kategorien");
		JButton button = new JButton(undrawAction);
		return button;
	}

	private void enableIfNoPlanned(Action action) {
		if (table.getSelectedRowCount() > 0) {
			for (int i = 0; i < table.getSelectedRowCount(); i++) {
				Category category = tableModel.getList().get(table.getSelectedRows()[i]);
				if (!category.isPlanned()) {
					action.setEnabled(false);
					return;
				}
			}
			action.setEnabled(true);
		} else {
			action.setEnabled(false);
		}
	}

	private boolean undraw() {
		Category[] categories = new Category[table.getSelectedRowCount()];
		for (int i = 0; i < categories.length; i++) {
			categories[i] = tableModel.getList().get(table.getSelectedRows()[i]);
			categories[i].removePlan();
		}

		tournamentHolder.fireChange();
		return true;
	}

	private JButton createPrintButton() {
		final Action printAction;
		printAction = new AbstractAction("Drucken") {
			public void actionPerformed(ActionEvent e) {
				print();
			}
		};
		printAction.setEnabled(table.getSelectedRowCount() > 0);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				enableIfNoPlanned(printAction);
			}
		});
		tournamentHolder.addListener(new TournamentListener() {
			public void tournamentChanged() {
				enableIfNoPlanned(printAction);
			}
		});
		printAction.putValue(Action.SHORT_DESCRIPTION, "Druckt die selektierten Kategorien");
		JButton button = new JButton(printAction);
		return button;
	}

	private void print() {
		List<Sheet> sheets = new ArrayList<Sheet>();
		for (int i = 0; i < table.getSelectedRowCount(); i++) {
			Category category = tableModel.getList().get(table.getSelectedRows()[i]);
			for (int j = 0; j < category.getLayout().getSheetCount(); j++) {
				Sheet sheet = new Sheet();
				sheet.category = category;
				sheet.sheetNo = j;
				sheets.add(sheet);
			}
			if (category.getLayout().getEndSheet() != null) {
				Sheet sheet = new Sheet();
				sheet.category = category;
				sheet.sheetNo = -1;
				sheets.add(sheet);
			}
			if (category.getLayout().getFinalrundeSheet() != null) {
				Sheet sheet = new Sheet();
				sheet.category = category;
				sheet.sheetNo = -2;
				sheets.add(sheet);
			}
		}
		if (new DrawingPrintable(getTournament(), sheets).isDone()) {
			Date now = new Date();
			for (int i = 0; i < table.getSelectedRowCount(); i++) {
				Category category = tableModel.getList().get(table.getSelectedRows()[i]);
				category.setPrintTime(now);
			}
			tournamentHolder.fireChange();
		}
	}

	//

	public void propertyChange(PropertyChangeEvent evt) {
		updateTableModel();
		Tournament t = (Tournament) tournamentHolder.getValue();
		tableModel.setList(t.getCategories());
	}

	public List<Category> getSelectedCategories() {
		if (table.getSelectedRowCount() == 0)
			return null;

		List<Category> selectedRows = new ArrayList<Category>(table.getSelectedRowCount());

		for (int i = 0; i < table.getSelectedRowCount(); i++) {
			Category category = tableModel.getList().get(table.getSelectedRows()[i]);
			selectedRows.add(category);
		}
		return selectedRows;
	}

	//

}